package com.atguigu.tingshu.album.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import com.atguigu.tingshu.album.mapper.AlbumInfoMapper;
import com.atguigu.tingshu.album.mapper.AlbumStatMapper;
import com.atguigu.tingshu.album.mapper.TrackInfoMapper;
import com.atguigu.tingshu.album.mapper.TrackStatMapper;
import com.atguigu.tingshu.album.service.AuditService;
import com.atguigu.tingshu.album.service.TrackInfoService;
import com.atguigu.tingshu.album.service.VodService;
import com.atguigu.tingshu.common.cache.GuiGuCache;
import com.atguigu.tingshu.common.constant.SystemConstant;

import com.atguigu.tingshu.model.album.AlbumInfo;
import com.atguigu.tingshu.model.album.AlbumStat;
import com.atguigu.tingshu.model.album.TrackInfo;
import com.atguigu.tingshu.model.album.TrackStat;
import com.atguigu.tingshu.query.album.TrackInfoQuery;
import com.atguigu.tingshu.user.client.UserFeignClient;
import com.atguigu.tingshu.vo.album.*;
import com.atguigu.tingshu.vo.user.UserInfoVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;

import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Slf4j
@Service
@SuppressWarnings({"all"})
public class TrackInfoServiceImpl extends ServiceImpl<TrackInfoMapper, TrackInfo> implements TrackInfoService {

    @Autowired
    private TrackInfoMapper trackInfoMapper;

    @Autowired
    private AlbumInfoMapper albumInfoMapper;

    @Autowired
    private VodService vodService;

    @Autowired
    private TrackStatMapper trackStatMapper;

    @Autowired
    private AuditService auditService;

    @Autowired
    private UserFeignClient userFeignClient;

    @Autowired
    private AlbumStatMapper albumStatMapper;


    @Override
    public void saveTrackInfo(TrackInfoVo trackInfoVo, Long userId) {
        //1.根据专辑id查询专辑信息,用于更新专辑声音数量
        Long albumId = trackInfoVo.getAlbumId();
        AlbumInfo albumInfo = albumInfoMapper.selectById(albumId);

        //2.新增声音记录
        //2.1 将声音VO转为PO
        TrackInfo trackInfo = BeanUtil.copyProperties(trackInfoVo, TrackInfo.class);


        trackInfo.setUserId(userId);
        trackInfo.setStatus(SystemConstant.TRACK_STATUS_NO_PASS);

        //2.2.2 设置声音序号 要求从1开始递增
        trackInfo.setOrderNum(albumInfo.getIncludeTrackCount() + 1);

        //如果用户没传图片,就复用专辑的图片
        if (StringUtils.isBlank(trackInfoVo.getCoverUrl())) {
            trackInfo.setCoverUrl(albumInfo.getCoverUrl());
        }

        trackInfo.setSource(SystemConstant.TRACK_SOURCE_USER);


        //2.2.3 调用点播平台获取音频详情信息：时长、大小、类型
        TrackMediaInfoVo trackMediaInfoVo = vodService.getMediaInfo(trackInfo.getMediaFileId());
        if (trackMediaInfoVo != null) {
            trackInfo.setMediaDuration(BigDecimal.valueOf(trackMediaInfoVo.getDuration()));
            trackInfo.setMediaSize(trackMediaInfoVo.getSize());
            trackInfo.setMediaType(trackMediaInfoVo.getType());
        }


        //2.3 新增声音记录
        trackInfoMapper.insert(trackInfo);

        //2.2.4 TODO 调用内容审核接口，发起内容审核，更新审核任务ID
        String text = trackInfo.getTrackTitle() + trackInfo.getTrackIntro();
        String suggestion = auditService.auditText(text);
        if ("pass".equals(suggestion)) {
            trackInfo.setStatus(SystemConstant.TRACK_STATUS_PASS);
            //TODO 文本审核通过后，还需要进一步审核音频内容
            String reviewTaskId = auditService.startReviewTask(trackInfo.getMediaFileId());
            trackInfo.setStatus(SystemConstant.TRACK_STATUS_REVIEWING);
            trackInfo.setReviewTaskId(reviewTaskId);
        } else if ("block".equals(suggestion)) {
            trackInfo.setStatus(SystemConstant.TRACK_STATUS_NO_PASS);
        } else if ("review".equals(suggestion)) {
            trackInfo.setStatus(SystemConstant.TRACK_STATUS_ARTIFICIAL);
        }
        trackInfoMapper.updateById(trackInfo);

        //3. 更新专辑信息：包含声音数量
        albumInfo.setIncludeTrackCount(albumInfo.getIncludeTrackCount() + 1);
        albumInfoMapper.updateById(albumInfo);

        //4.新增声音统计信息
        this.saveTrackStat(trackInfo.getId(), SystemConstant.TRACK_STAT_PLAY, 0);
        this.saveTrackStat(trackInfo.getId(), SystemConstant.TRACK_STAT_COLLECT, 0);
        this.saveTrackStat(trackInfo.getId(), SystemConstant.TRACK_STAT_PRAISE, 0);
        this.saveTrackStat(trackInfo.getId(), SystemConstant.TRACK_STAT_COMMENT, 0);


    }

    /**
     * 保存声音统计信息
     *
     * @param trackId  声音ID
     * @param statType 统计类型
     * @param statNum  统计数值
     */
    @Override
    public void saveTrackStat(Long trackId, String statType, int statNum) {
        TrackStat trackStat = new TrackStat();
        trackStat.setTrackId(trackId);
        trackStat.setStatType(statType);
        trackStat.setStatNum(statNum);
        trackStatMapper.insert(trackStat);
    }

    @Override
    public Page<TrackListVo> findUserTrackPage(Page<TrackListVo> page1, TrackInfoQuery trackInfoQuery) {
        return trackInfoMapper.getUserTrackPage(page1, trackInfoQuery);
    }

    /**
     * 查询声音信息,用于修改声音时回显数据
     *
     * @param id
     * @return
     */
    @Override
    public TrackInfo getTrackInfo(Long id, Long userId) {

        TrackInfo trackInfo = trackInfoMapper.selectById(id);

        trackInfo.setUserId(userId);


        return trackInfo;
    }


    /**
     * 修改声音信息
     *
     * @param id
     * @param trackInfo
     * @return
     */
    @Override
    public void updateTrackInfo(Long id, TrackInfo trackInfo, Long userId) {

        trackInfo.setUserId(userId);

        //判断音频文件是否被修改,修改了就需要重新去云点播平台获取新的音频文件标识
        TrackInfo oldTrackInfo = trackInfoMapper.selectById(id);

        //对文本进行审核
        String text = trackInfo.getTrackTitle() + trackInfo.getTrackIntro();
        String suggestion = auditService.auditText(text);
        if ("pass".equals(suggestion)) {
            trackInfo.setStatus(SystemConstant.TRACK_STATUS_PASS);
        } else if ("block".equals(suggestion)) {
            trackInfo.setStatus(SystemConstant.TRACK_STATUS_NO_PASS);
        } else if ("review".equals(suggestion)) {
            trackInfo.setStatus(SystemConstant.TRACK_STATUS_ARTIFICIAL);
        }

        if (!oldTrackInfo.getMediaFileId().equals(trackInfo.getMediaFileId())) {
            //1.3 如果文件被更新，再次获取新音频文件信息更新：时长，大小，类型
            TrackMediaInfoVo mediaInfo = vodService.getMediaInfo(trackInfo.getMediaFileId());

            Float duration = mediaInfo.getDuration();
            String mediaUrl = mediaInfo.getMediaUrl();
            Long mediaSize = mediaInfo.getSize();
            String mediaType = mediaInfo.getType();

            // todo 音频文件发生更新后，必须再次进行审核
            String reviewTaskId = auditService.startReviewTask(trackInfo.getMediaFileId());
            trackInfo.setStatus(SystemConstant.TRACK_STATUS_REVIEWING);
            trackInfo.setReviewTaskId(reviewTaskId);
            //1.4 从点播平台删除旧的音频文件
            vodService.deleteMedia(oldTrackInfo.getMediaFileId());
        }

        trackInfo.setTrackTitle(trackInfo.getTrackTitle());
        trackInfo.setTrackIntro(trackInfo.getTrackIntro());
        trackInfo.setCoverUrl(trackInfo.getCoverUrl());


        //2.更新声音信息
        trackInfoMapper.updateById(trackInfo);
    }


    /**
     * 删除声音信息
     *
     * @param id
     * @return
     */
    @Override
    public void removeTrackInfo(Long id) {

        //1.删除声音信息
        //1.1首先获取要删除的对象,得到云点播平台的文件标识
        TrackInfo trackInfo = trackInfoMapper.selectById(id);
        String mediaFileId = trackInfo.getMediaFileId();

        //此外还要修改专辑信息表,因为要保证专辑内的音频文件的序号是有序的
        //比如1,2,3,4,5, 我删除了3号声音,就需要将3号之后的标号统一减一

        Integer orderNum = trackInfo.getOrderNum();
        Long albumId = trackInfo.getAlbumId();

        LambdaUpdateWrapper<TrackInfo> trackInfoUpdateWrapper = new LambdaUpdateWrapper<>();

        trackInfoUpdateWrapper.eq(TrackInfo::getAlbumId, albumId);
        trackInfoUpdateWrapper.gt(TrackInfo::getOrderNum, orderNum);

        trackInfoUpdateWrapper.setSql("order_num = order_num - 1");

        trackInfoMapper.update(null, trackInfoUpdateWrapper);

        //还有就是将专辑表的声音数量减1
        AlbumInfo albumInfo = albumInfoMapper.selectById(albumId);
        albumInfo.setIncludeTrackCount(albumInfo.getIncludeTrackCount() - 1);
        albumInfoMapper.updateById(albumInfo);


        //2.删除声音统计信息
        LambdaQueryWrapper<TrackStat> deleteWrapper = new LambdaQueryWrapper<>();

        deleteWrapper.eq(TrackStat::getTrackId, id);

        trackStatMapper.delete(deleteWrapper);


        //3.删除声音在云点播平台上的记录

        vodService.deleteMedia(mediaFileId);

        //在最后删除声音信息
        trackInfoMapper.deleteById(id);
    }

    /**
     * 需求：用户未登录，可以给用户展示声音列表；用户已登录，可以给用户展示声音列表，并动态渲染付费标识
     * 分页查询专辑下声音列表（动态渲染付费标识）
     *
     * @param albumId
     * @param page
     * @param limit
     * @return
     */
    @Override
    public Page<AlbumTrackListVo> findAlbumTrackPage(Long albumId, Page<AlbumTrackListVo> voPage, Long userId) {


        Page<AlbumTrackListVo> page = trackInfoMapper.findAlbumTrackPage(albumId, voPage);

        //如果用户没有登录应该为 VIP 免费专辑和 付费专辑 的试听集数以外的声音加上付费标识

        //首先获取到专辑的 pay_type 和 免费试听集数
        AlbumInfo albumInfo = albumInfoMapper.selectById(albumId);
        //付费类型
        String payType = albumInfo.getPayType();
        //免费试听集数
        Integer tracksForFree = albumInfo.getTracksForFree();

        if (userId == null) {
            //走到这说明用户未登录
            if (SystemConstant.ALBUM_PAY_TYPE_VIPFREE.equals(payType) || SystemConstant.ALBUM_PAY_TYPE_REQUIRE.equals(payType)){
				List<AlbumTrackListVo> albumTrackListVos = voPage.getRecords();

				albumTrackListVos.stream()
						.filter(vo -> vo.getOrderNum() > tracksForFree)
						.forEach(vo -> vo.setIsShowPaidMark(true));
			}
        }else {
            /*走到这说明用户登陆了,登陆了情况就复杂了:
            *   1.涉及到用户是否为VIP
            *       1.1 如果用户是普通用户且此专辑不是必须付费(即Vip免费,),需要判断用户是否购买过专辑或声音,所以需要调用远程接口
            *       1.2 如果用户是VIP用户就都免费
            *   2.还有就是如果是付费专辑,那么普通用户和VIP一视同仁
            *   3.但是付费也分两种,一种是专辑必须买整个专辑,一种是可以分开买声音
            *
            * */

            //1.第一步先远程调用用户微服务获取用户信息,判断是不是Vip用户
            UserInfoVo userInfoVo = userFeignClient.getUserInfoVo(userId).getData();
            Integer isVip = userInfoVo.getIsVip();

            Assert.notNull(userInfoVo, "用户{}不存在", userId);

            Boolean isVIP = false;

            if (isVip == 1){
                //走到这说明是普通用户将isVIP设置为false
                isVIP = true;
            }

            //2.然后判断该专辑的情况是免费还是付费还是VIP免费

            //获取当前页中除试听以外其他声音ID列表
            List<Long> needCheckPayStatusTrackIdList = page
                    .getRecords()
                    .stream()
                    .filter(t -> t.getOrderNum() > tracksForFree)
                    .map(AlbumTrackListVo::getTrackId)
                    .collect(Collectors.toList());

            //2.1免费的情况
            if (SystemConstant.ALBUM_PAY_TYPE_FREE.equals(payType)){
                //将所有声音设为免费
                List<AlbumTrackListVo> albumTrackListVoList = voPage.getRecords();

                albumTrackListVoList.stream().filter(vo -> vo.getOrderNum() > tracksForFree).forEach(
                        vo -> vo.setIsShowPaidMark(false)
                );
            }

            //2.VIP免费
            if (SystemConstant.ALBUM_PAY_TYPE_VIPFREE.equals(payType)){
                if (!isVIP){
                    //走到这说明是普通用户,需要将免费试听集数后面的设置为付费
                    //另外如果普通用户购买了单个声音,也需要判断
                    List<AlbumTrackListVo> albumTrackListVos = voPage.getRecords();
                    //远程调用检查购买接口查询购买情况

                    //mapResult所记录的就是购买过的音乐的id
                    Map<Long, Integer> mapResult = userFeignClient.userIsPaidTrack(userId, albumId, needCheckPayStatusTrackIdList).getData();

                    if (CollUtil.isNotEmpty(mapResult)){
                        //走到这说明购买过
                        albumTrackListVos.stream()
                                .filter(vo -> vo.getOrderNum() > tracksForFree)
                                //这句循环的就是将没购买的设置为true
                                .forEach(vo -> vo.setIsShowPaidMark(mapResult.get(vo.getTrackId()) == 0));
                    }

                    albumTrackListVos.stream()
                            .filter(vo -> vo.getOrderNum() > tracksForFree)
                            .forEach(vo -> vo.setIsShowPaidMark(true));
                }else {
                    //走到这说明是VIP,全免费
                    List<AlbumTrackListVo> albumTrackListVoList = voPage.getRecords();

                    albumTrackListVoList.stream().filter(vo -> vo.getOrderNum() > tracksForFree).forEach(
                            vo -> vo.setIsShowPaidMark(false)
                    );
                }
            }


            //3.付费的专辑,一视同仁
            if (SystemConstant.ALBUM_PAY_TYPE_REQUIRE.equals(payType)){

                List<AlbumTrackListVo> albumTrackListVos = voPage.getRecords();

                //不管是不是vip用户都需要直接判断购没购买过专辑
                //远程调用检查购买接口查询购买情况
                Map<Long, Integer> mapResult = userFeignClient.userIsPaidTrack(userId, albumId, needCheckPayStatusTrackIdList).getData();

                if (CollUtil.isNotEmpty(mapResult)){
                    //走到这说明购买过
                    albumTrackListVos.stream()
                            .filter(vo -> vo.getOrderNum() > tracksForFree)
                            //这句循环的就是将没购买的设置为true
                            .forEach(vo -> vo.setIsShowPaidMark(mapResult.get(vo.getTrackId()) == 0));
                }

            }

        }

        return page;
    }


    /**
     * 获取声音统计信息
     * @param trackId
     * @return
     */
    @Override
    public TrackStatVo getTrackStatVo(Long trackId) {

        TrackStatVo trackStatVo = trackInfoMapper.getTrackStatVo(trackId);

        return trackStatVo;
    }


    /**
     * 更新声音/专辑统计信息
     *
     * @param mqVo
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateTrackStat(TrackStatMqVo mqVo) {

        //首先分析一下对声音的某些统计类型做修改需不需要对专辑一并修改
        //专辑的统计类型：0401-播放量 0402-订阅量 0403-购买量 0403-评论数
        //声音的统计类型：0701-播放量 0702-收藏量 0703-点赞量 0704-评论数

        //由上可见,只有声音的播放量和评论数会影响到专辑的统计数量,所以需要一并修改

        //1.先修改声音表的统计数量

        LambdaUpdateWrapper<TrackStat> trackStatUpdateWrapper = new LambdaUpdateWrapper<>();

        trackStatUpdateWrapper.eq(TrackStat::getTrackId,mqVo.getTrackId());
        trackStatUpdateWrapper.eq(TrackStat::getStatType,mqVo.getStatType());
        trackStatUpdateWrapper.setSql("stat_num = stat_num + "+ mqVo.getCount());

        trackStatMapper.update(null,trackStatUpdateWrapper);

        //2.判断类型,更新专辑表
        if(SystemConstant.TRACK_STAT_PLAY.equals(mqVo.getStatType())){
            LambdaUpdateWrapper<AlbumStat> albumStatUpdateWrapper = new LambdaUpdateWrapper<>();
            albumStatUpdateWrapper.eq(AlbumStat::getAlbumId,mqVo.getAlbumId());
            albumStatUpdateWrapper.eq(AlbumStat::getStatType,mqVo.getStatType());
            albumStatUpdateWrapper.setSql("stat_num = stat_num +" + mqVo.getCount());
            albumStatMapper.update(null,albumStatUpdateWrapper);
        }

        if (SystemConstant.TRACK_STAT_COMMENT.equals(mqVo.getStatType())){

            albumStatMapper.update(null,
                    new LambdaUpdateWrapper<AlbumStat>()
                            .eq(AlbumStat::getAlbumId,mqVo.getAlbumId())
                            .eq(AlbumStat::getStatType,mqVo.getStatType())
                            .setSql("stat_num = stat_num +" + mqVo.getCount())
                    );
        }

    }


    /**
     * 基于用户选择声音作为起始标准获取未购买声音数量，得到分集购买列表
     *
     * @param trackId
     * @return [{name:"本集",price:0.1,trackCount:1},{name:"后10集",price:1,trackCount:10},...,{name:"全集",price:4.3,trackCount:43},]
     */
    @Override
    public List<Map<String, Object>> findUserTrackPaidList(Long userId, Long trackId) {
        //1.根据声音ID查询到“起始”选择购买声音记录,获取专辑ID跟序号
        TrackInfo trackInfo = trackInfoMapper.selectById(trackId);
        Long albumId = trackInfo.getAlbumId();
        Integer orderNum = trackInfo.getOrderNum();

        //2.根据"起始"声音序号+专辑ID获取所有“待购买”声音列表（可能包含已购买声音）
        List<TrackInfo> waitBuyTrackList = trackInfoMapper.selectList(
                new LambdaQueryWrapper<TrackInfo>()
                        .eq(TrackInfo::getAlbumId, albumId)
                        .ge(TrackInfo::getOrderNum, orderNum)
                        .select(TrackInfo::getId)
        );

        //3.远程调用用户服务获取已购买声音ID列表
        List<Long> userPaidTrackIdList = userFeignClient.findUserPaidTrackList(albumId).getData();
        if (CollUtil.isNotEmpty(userPaidTrackIdList)) {
            //4.如果存在已购声音ID，则将已购声音ID从待购买声音列表中移除，得到未购买声音数量
            waitBuyTrackList = waitBuyTrackList.stream()
                    .filter(track -> !userPaidTrackIdList.contains(track.getId()))
                    .collect(Collectors.toList());
        }
        //5.基于未购买声音数量，生成分集购买列表
        int size = waitBuyTrackList.size();
        AlbumInfo albumInfo = albumInfoMapper.selectById(albumId);
        BigDecimal price = albumInfo.getPrice();
        //5.1 创建分集购买集合List
        List<Map<String, Object>> list = new ArrayList<>();
        //5.2 构建"本集"分集购买对象
        Map<String, Object> currMap = new HashMap<>();
        currMap.put("name", "本集");
        currMap.put("price", price);
        currMap.put("trackCount", 1);
        list.add(currMap);
        //5.3 构建其他"后*集"分集购买对象   未购买数量：23    43
        for (int i = 10; i <= 50; i += 10) {
            Map<String, Object> map = new HashMap<>();
            if (size >= i) {
                map.put("name", "后" + i + "集");
                map.put("price", price.multiply(new BigDecimal(i)));
                map.put("trackCount", i);
                list.add(map);
            } else {
                map.put("name", "全集(后" + size + "集)");
                map.put("price", price.multiply(new BigDecimal(size)));
                map.put("trackCount", size);
                list.add(map);
                break;
            }
        }
        return list;
    }


    /**
     * 基于选择购买声音ID作为起始获取指定数量未购买声音列表
     *
     * @param trackId
     * @param trackCount
     * @return
     */
    @Override
    public List<TrackInfo> findPaidTrackInfoList(Long userId, Long trackId, Integer trackCount) {
        //1.根据选择声音ID查询声音，得到专辑ID跟序号
        TrackInfo trackInfo = trackInfoMapper.selectById(trackId);
        Integer orderNum = trackInfo.getOrderNum();
        Long albumId = trackInfo.getAlbumId();

        //2.远程调用用户服务获取已购买声音ID列表
        List<Long> userPaidTrackIdList = userFeignClient.findUserPaidTrackList(albumId).getData();


        //2.获取指定数量未购买声音列表
        LambdaQueryWrapper<TrackInfo> queryWrapper = new LambdaQueryWrapper<TrackInfo>()
                .eq(TrackInfo::getAlbumId, albumId)
                .ge(TrackInfo::getOrderNum, orderNum)
                .last("limit " + trackCount)
                .select(TrackInfo::getId,TrackInfo::getTrackTitle, TrackInfo::getCoverUrl, TrackInfo::getAlbumId);
        if (CollUtil.isNotEmpty(userPaidTrackIdList)) {
            queryWrapper.notIn(TrackInfo::getId, userPaidTrackIdList);
        }
        List<TrackInfo> trackInfoList = trackInfoMapper.selectList(queryWrapper);
        return trackInfoList;
    }
}
